package cn.itsource.config;

import cn.itsource.basic.aop.LogAop;
import cn.itsource.domain.Log;
import cn.itsource.mapper.LogMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.Arrays;

//@Aspect 开启切面
//@Component 交给spring容器管理
@Aspect
@Component
@Slf4j
public class MyLogAop {

    @Autowired
    LogMapper logMapper;

    private static String[] types = { "java.lang.Integer", "java.lang.Double",
            "java.lang.Float", "java.lang.Long", "java.lang.Short",
            "java.lang.Byte", "java.lang.Boolean", "java.lang.Char",
            "java.lang.String", "int", "double", "long", "short", "byte",
            "boolean", "char", "float" };

    @Pointcut("@annotation(myLog)")
    public void cut(LogAop myLog){

    }

//    在方法之前执行
    @Before("cut(Object)")
    public void before(JoinPoint joinPoint) throws NotFoundException {
        //RequestContextHolder  从tomcat里面拿到 ServletRequest 拿 HttpServletRequest
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //拿到对应的url
        String url = request.getRequestURI();
        //拿到对应的ip   0.0.0.0.0.1
        String ip = request.getRemoteHost();
        String remoteAddr = request.getRemoteAddr();
        //拿到所有参数的名称
//        Enumeration<String> attributeNames = request.getAttributeNames();
//        //获取所有的参数
//        StringBuilder stringBuilder = new StringBuilder();
//
//        while (attributeNames.hasMoreElements()){
//            stringBuilder.append("参数名:")
//                    .append(attributeNames.nextElement())
//                    .append("参数值:")
//                    .append(request.getAttribute(attributeNames.nextElement()));
//        }
//        String s = stringBuilder.toString();
        Object[] argss = joinPoint.getArgs();
        String args = argss.toString();
        //获取对应的类以及方法
        Object target = joinPoint.getTarget();
        Class<?> aClass = target.getClass();
        //获取当前类
        String typeName = aClass.getTypeName();
        //获取当前方法
        String method = joinPoint.getSignature().getName();
        //这里获取到的是当前方法的对应所有参数名称 page  pageSize keyword
        String[] paramNames = getFieldsName(this.getClass(), typeName, method);
        String returnValue = getReturnValue(paramNames, joinPoint);
        log.info("获取到当前的ip:{},获取到当前的url:{},当前获取到的参数以及参数名称:{},当前获取的返回值:{}",ip,url,paramNames,returnValue);
        //获取session
        HttpSession session = request.getSession();
        //Employee employee = (Employee) session.getAttribute(Constant.USER_SESSION);
        String userId = "0";
        String s = Arrays.deepToString(paramNames);
        //获取mapper
//        SpringUtil.getBean()
      //  if (Objects.nonNull(employee)){
       //     id = employee.getId();
       // }
        logMapper.insert(new Log(url,ip,typeName,method,returnValue,userId,null));

    }

    private static String getReturnValue(String[] paramNames, JoinPoint joinPoint){
        String typeName = null;
        Object[] args = joinPoint.getArgs();
        StringBuilder sb = new StringBuilder();
        boolean clazzFlag = true;
        for(int k=0; k<args.length; k++){
            Object arg = args[k];
            sb.append(paramNames[k]+" ");
            // 获取对象类型
            if (arg==null){
                arg="null";
            }else{ typeName= arg.getClass().getTypeName();}

            for (String t : types) {
                if (t.equals(typeName)) {
                    sb.append("=" + arg+"; ");
                }
            }
            if (clazzFlag) {
                sb.append(getFieldsValue(arg));
            }
        }
        return sb.toString();
    }

    /**
     * 得到参数的值
     * @param obj
     */
    public static String getFieldsValue(Object obj) {
        Field[] fields = obj.getClass().getDeclaredFields();
        String typeName = obj.getClass().getTypeName();
        for (String t : types) {
            if(t.equals(typeName)) {
                return "";
            }
        }
        StringBuilder sb = new StringBuilder();
        sb.append("【");
        for (Field f : fields) {
            f.setAccessible(true);
            try {
                for (String str : types) {
                    if (f.getType().getName().equals(str)){
                        sb.append(f.getName() + " = " + f.get(obj)+"; ");
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        sb.append("】");
        return sb.toString();
    }
    /**
     * 得到方法参数的名称
     * @param cls
     * @param clazzName
     * @param methodName
     * @return
     * @throws
     */
    private static String[] getFieldsName(Class cls, String clazzName, String methodName) throws NotFoundException {
//        Field[] declaredFields = this.getClass().getDeclaredFields();
//        for (Field field : declaredFields){
//            field.getName();
//        }
        ClassPool pool = ClassPool.getDefault();
        //ClassClassPath classPath = new ClassClassPath(this.getClass());
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < paramNames.length; i++){
            paramNames[i] = attr.variableName(i + pos);    //paramNames即参数名
        }
        return paramNames;
    }
}